#    mycharts.py  
from reportlab.graphics.shapes import Drawing, String
from reportlab.graphics.charts.lineplots import LinePlot
from reportlab.graphics.charts.lineplots import ScatterPlot
from reportlab.lib import colors
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.widgets.markers import makeMarker
from fiteat.utils import get_units
from django.http import HttpResponse
from reportlab.graphics.charts.utils import seconds2str
from time import strftime, gmtime
from datetime import date, timedelta, MAXYEAR, MINYEAR 
from itertools import repeat 
from django.utils.translation import ugettext as _

NUM_OF_DAYS_MARGIN = 3
NUM_OF_X_STEPS = 7 # Number of labels below the X axis

class ChartError(Exception):
    def __init__(self, val):
        self.val = val

    def __str__(self):
        return repr(self.val)


class MyStringDrawing(Drawing):
    def __init__(self, width=600, height=400, title='', *args, **kw):
        apply(Drawing.__init__,(self,width,height)+args,kw)

        self.add(String(200,180,''), name='title')
        self.title.fontName = 'Times-Roman'
        self.title.fontSize = 18
        self.title.x = self.width/2 - 40
        self.title.y = self.height - 30
        self.title.textAnchor ='middle'
        self.title.text=title

class MyLineChartDrawing(Drawing):
    def __init__(self, width=600, height=400, *args, **kw):
        apply(Drawing.__init__,(self,width,height)+args,kw)
        self.add(LinePlot(), name='chart')

        self.add(String(200,180,'Hello World'), name='title')

        #set any shapes, fonts, colors you want here.  We'll just
        #set a title font and place the chart within the drawing.
        #pick colors for all the lines, do as many as you need
        self.chart.x = 40
        self.chart.y = 40
        self.chart.width = self.width - 140
        self.chart.height = self.height - 75
        self.chart.lines[0].strokeColor = colors.blue
        self.chart.lines[1].strokeColor = colors.green
        self.chart.lines[2].strokeColor = colors.red
        self.chart.lines[3].strokeColor = colors.yellow
        self.chart.lines[4].strokeColor = colors.black
        self.chart.lines[5].strokeColor = colors.orange
        self.chart.lines[6].strokeColor = colors.cyan
        self.chart.lines[7].strokeColor = colors.magenta
        self.chart.lines[8].strokeColor = colors.brown
	
        self.chart.fillColor = colors.white
        self.title.fontName = 'Times-Roman'
        self.title.fontSize = 18
        self.chart.xValueAxis.labels.fontSize = 12
        self.chart.xValueAxis.forceZero = 0
        self.chart.xValueAxis.gridEnd = 115
        self.chart.xValueAxis.tickDown = 3
        self.chart.xValueAxis.visibleGrid = 1
        self.chart.yValueAxis.visibleGrid = 1
        self.chart.yValueAxis.tickLeft = 3
        self.chart.yValueAxis.labels.fontName = 'Times-Roman'
        self.chart.yValueAxis.labels.fontSize = 12
        self.title.x = self.width/2 - 40
        self.title.y = self.height - 30
        self.title.textAnchor ='middle'
        self.add(Legend(),name='Legend')
        self.Legend.fontName = 'Times-Roman'
        self.Legend.fontSize = 12
        self.Legend.x = self.width-90
        self.Legend.y = 185
        self.Legend.dxTextSpace = 5
        self.Legend.dy = 5
        self.Legend.dx = 5
        self.Legend.deltay = 1
        self.Legend.alignment ='right'
        self.Legend.columnMaximum = 10
        self.add(Label(),name='XLabel')
        self.XLabel.fontName = 'Times-Roman'
        self.XLabel.fontSize = 12
        self.XLabel.x = 485
        self.XLabel.y = 15
        self.XLabel.textAnchor ='middle'
        #self.XLabel.height = 20
        self.XLabel._text = ""
        self.add(Label(),name='YLabel')
        self.YLabel.fontName = 'Times-Roman'
        self.YLabel.fontSize = 12
        self.YLabel.x = 2
        self.YLabel.y = 180
        self.YLabel.angle = 90
        self.YLabel.textAnchor ='middle'
        self.YLabel._text = ""
        #self.chart.yValueAxis.forceZero = 1


def graph(request, fields, period, prefs, orig_data, known_fields):
    try:
        weight, length, distance = get_units(request.user.get_profile())
        start_date = None
        if period == 'year':
            start_date = date.today() - timedelta(365)
        elif period == 'month':
            start_date = date.today() - timedelta(30)

        if start_date:
            orig_data = orig_data.filter(date__range=(start_date, date.today()))

        #instantiate a drawing object
        d = MyLineChartDrawing()

        fields_list = []
        if fields.startswith('custom_chart'):
            fields_list = [eval('prefs.%s_field1' % fields), eval('prefs.%s_field2' % fields), eval('prefs.%s_field3' % fields)]
        else:
            fields_list = fields.split('+')
        field_names = []
        field_names_with_units = []
        data = []
        date_min = date(MAXYEAR, 12, 31)
        date_max = date(MINYEAR, 1, 1)
        val_max = 0.0
        val_min = 10000.0
        d.chart.data = []
        labels = []

        for field in fields_list:
            if field not in known_fields:
                raise ChartError("Unknown field") 
            type = known_fields[field]

            field_name = field.title()
            if type == 'weight':
                units = ' (%s)' % weight
            elif type == 'length':
                units = ' (%s)' % length
            elif type == 'distance':
                units = ' (%s)' % distance
            elif type == 'time':
                units = _(' (mins)')
            elif type == 'custom':
                field_name = eval('prefs.%s_caption' % field).title()
                if len(field_name) == 0:
                    field_name = field.title()
                units = ''
            else:
                units = ''

            field_names.append(field_name)
            field_names_with_units.append('%s%s' % (field_name, units))

            dates = []
            vals = []
            trends = []
            for m in orig_data:
                val = eval('m.%s' % field)
                trend = eval('m.%s_trend' % field)
                if val != None:
                    val = float(val)
                    dates.append(m.date.strftime('%d/%m/%Y'))
                    vals.append(val)
                    if m.date < date_min:
                        date_min = m.date
                    if m.date > date_max:
                        date_max = m.date
                    if val > val_max:
                        val_max = val
                    if val < val_min:
                        val_min = val
                    trend = float(trend)
                    if trend > val_max:
                        val_max = trend
                    if trend < val_min:
                        val_min = trend
                    trends.append(float(trend))
            if len(vals) < 2:
                raise ChartError(_("Need at least two data points in order to show chart"))
            d.chart.data += [zip(dates,vals), zip(dates, trends)]
            goal = eval('prefs.%s_goal' % field)
            if goal != None:
                goal = float(goal)
                if goal < val_min:
                    val_min = goal
                if goal > val_max:
                    val_max = goal
                d.chart.data.append(zip(dates, repeat(goal)))

            labels +=  [field_name, '%s\'s Trend' % field_name]
            if goal:
                labels.append('%s\'s Goal' % field_name)

        if labels:
            cnt=0
            # set colors in the legend
            d.Legend.colorNamePairs = []
            for label in labels:
                    d.Legend.colorNamePairs.append((d.chart.lines[cnt].strokeColor,label))
                    cnt=cnt+1

        d.title.text = '%s for %s' % (', '.join(field_names), request.user)
        d.XLabel._text = _('Date')
        d.YLabel._text = ', '.join(field_names_with_units)

        xLabels = []
        dx_days = ((date_max - date_min).days + NUM_OF_DAYS_MARGIN * 2) / 7
        for i in xrange(NUM_OF_X_STEPS):
            xLabels.append(int((date_min + timedelta(i * dx_days - NUM_OF_DAYS_MARGIN)).strftime('%s')))
        xLabels.append(int(date_max.strftime('%s')))

        d.chart.xValueAxis.valueMin = xLabels[0]
        d.chart.xValueAxis.valueMax = xLabels[-1]
        d.chart.xValueAxis.valueSteps = xLabels

        # If the single cell of a grid is wider than a month, only specify
        # the month (not the full date)
        if dx_days < 30:
            d.chart.xValueAxis.labelTextFormat = seconds2str
        else:
            d.chart.xValueAxis.labelTextFormat = seconds2months

        d.chart.yValueAxis.valueMin = int(val_min) - 2
        d.chart.yValueAxis.valueMax = int(val_max) + 2
        d.chart.yValueAxis.valueStep = float(int(val_max) - int(val_min) + 4)/5
        d.chart.yValueAxis.labelTextFormat = '%d'


        #get a GIF (or PNG, JPG, or whatever)
        binaryStuff = d.asString('gif')
        return HttpResponse(binaryStuff, 'image/gif')
        
    except ChartError, e:
        d = MyStringDrawing(title=str(e))
        binaryStuff = d.asString('gif')
        return HttpResponse(binaryStuff, 'image/gif')

def seconds2months(seconds):
    return strftime('%b %Y', gmtime(seconds))

